home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / xvisrc.zip / TERMCAP.C < prev    next >
C/C++ Source or Header  |  1992-07-28  |  26KB  |  1,139 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)termcap.c    2.1 (Chris & John Downey) 7/29/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     termcap.c
  14. * module function:
  15.     Termcap terminal interface module.
  16.  
  17.     The following capabilities are not yet used, but may be added in future
  18.     to improve efficiency (at the expense of code compactness):
  19.  
  20.     cr    str    Carriage return, (default ^M)
  21.     dC    num    Number of milliseconds of cr delay needed
  22.     nc    bool    No correctly working carriage return (DM2500,H2000)
  23.     xr    bool    Return acts like ce \r \n (Delta Data)
  24.  
  25.     ch    str    Like cm but horizontal motion only, line stays same
  26.     DO    str    down N lines
  27.     up    str    Upline (cursor up)
  28.     UP    str    up N lines
  29.     LE    str    left N chars
  30.     RI    str    right N spaces
  31.     ll    str    Last line, first column
  32.     sc    str    save cursor
  33.     rc    str    restore cursor from last "sc"
  34.     dB    num    Number of milliseconds of bs delay needed
  35.  
  36.     AL    str    add N new blank lines
  37.     DL    str    delete N lines
  38.  
  39.     dc    str    Delete character
  40.     dm    str    Delete mode (enter)
  41.     ed    str    End delete mode
  42.     ip    str    Insert pad after character inserted
  43.     in    bool    Insert mode distinguishes nulls on display
  44.     mi    bool    Safe to move while in insert mode
  45.  
  46.     dF    num    Number of milliseconds of ff delay needed
  47.     cd    str    Clear to end of display
  48.  
  49.     xs    bool    Standout not erased by writing over it (HP 264?)
  50.     ms    bool    Safe to move while in standout and underline mode
  51.  
  52. * history:
  53.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  54.     Originally by Tim Thompson (twitch!tjt)
  55.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  56.     Heavily modified by Chris & John Downey
  57.  
  58. ***/
  59.  
  60. #include "xvi.h"
  61.  
  62. /* #define    SG_TEST    1 */
  63.  
  64. static    void    xyupdate P((void));
  65. static    void    fail P((char *));
  66. static    void    set_scroll_region P((int, int));
  67.  
  68. /*
  69.  * These are used to optimise output - they hold the current
  70.  * "real" and "virtual" coordinates of the cursor, i.e. where
  71.  * it is and where it is supposed to be.
  72.  *
  73.  * The "optimise" variable says whether we should cache cursor
  74.  * positions or should just go to the place asked; it is
  75.  * unset at the start so that the first goto sets things up.
  76.  *
  77.  * Note that the functions "outchar" and "outstr" should be
  78.  * used for strings of ordinary characters; stdio primitives
  79.  * are used internally to put out escape sequences.
  80.  */
  81. static    int    real_row = 0, real_col = 0;
  82. static    int    virt_row = 0, virt_col = 0;
  83. static    bool_t    optimise = FALSE;
  84.  
  85. /*
  86.  * Termcap-related declarations.
  87.  */
  88. extern    char    *tgetstr();
  89. extern    char    *tgoto();
  90.  
  91. /*
  92.  * Exported.
  93.  */
  94. int        cost_goto = 0;        /* cost of doing a goto */
  95. bool_t        can_scroll_area = FALSE; /* true if we can set scroll region */
  96. bool_t        can_del_line;        /* true if we can delete lines */
  97. bool_t        can_ins_line;        /* true if we can insert lines */
  98. bool_t        can_inschar;        /* true if we can insert characters */
  99. unsigned int    CO = 0;            /* screen dimensions; 0 at start */
  100. unsigned int    LI = 0;
  101.  
  102. /*
  103.  * Needed by termcap library.
  104.  */
  105. char    PC;                /* pad character */
  106.  
  107. /*
  108.  * Internal string, num and boolean defs.
  109.  */
  110. static    char    *KS, *KE;        /* keypad transmit start/end */
  111. static    char    *VS, *VE;        /* visual start/end */
  112. static    char    *TI, *TE;        /* cursor motion start/end */
  113. static    char    *CE, *CL;        /* erase line/display */
  114. static    char    *AL, *DL;        /* insert/delete line */
  115. static    char    *IC, *IM, *EI;        /* insert character / insert mode */
  116. static    char    *CM;            /* cursor motion string */
  117. static    char    *HO;            /* cursor to home position */
  118. static    char    *CS;            /* change scroll region */
  119. static    char    *sf, *sr;        /* scroll forward/reverse 1 line */
  120. static    char    *SF, *SR;        /* scroll forward/reverse n lines */
  121. static    char    *SO, *SE;        /* standout mode start/end */
  122.  
  123. static    char    *VB;            /* visual bell */
  124.  
  125. static    char    *colours[10];        /* colour caps c0 .. c9 */
  126. static    int    ncolours;        /* number of colour caps we have */
  127.  
  128. static    char    BC;            /* backspace char */
  129. static    char    ND;            /* backspace char */
  130. static    char    DO;            /* down one line */
  131.  
  132. static    bool_t    can_backspace;        /* true if can backspace (bs/bc) */
  133. static    bool_t    can_fwdspace = FALSE;    /* true if can forward space (nd) */
  134. static    bool_t    can_movedown = FALSE;    /* true if can move down (do) */
  135. static    bool_t    auto_margins;        /* true if AM is set */
  136.  
  137. /*
  138.  * We use this table to perform mappings from cursor keys
  139.  * into appropriate xvi input keys (in command mode).
  140.  */
  141. static char arrow_keys[] = {
  142.     K_UARROW,    '\0',
  143.     K_DARROW,    '\0',
  144.     K_RARROW,    '\0',
  145.     K_LARROW,    '\0',
  146.     CTRL('B'),    '\0',
  147.     CTRL('F'),    '\0',
  148.     K_HELP,    '\0',
  149. };
  150. static struct {
  151.     char    *key_tcname;
  152.     char    *key_rhs;
  153. } keys[] = {
  154.     "ku",    arrow_keys + 0,    /* up */
  155.     "kd",    arrow_keys + 2,    /* down */
  156.     "kr",    arrow_keys + 4,    /* right */
  157.     "kl",    arrow_keys + 6,    /* left */
  158.     "kP",    arrow_keys + 8,    /* page up */
  159.     "kN",    arrow_keys + 10,/* page down */
  160.     "kh",    "H",        /* home */
  161.     "k0",    "#0",        /* function key 0 */
  162.     "k1",    arrow_keys + 12,/* help */
  163.     "k2",    "#2",        /* function key 2 */
  164.     "k3",    "#3",        /* function key 3 */
  165.     "k4",    "#4",        /* function key 4 */
  166.     "k5",    "#5",        /* function key 5 */
  167.     "k6",    "#6",        /* function key 6 */
  168.     "k7",    "#7",        /* function key 7 */
  169.     "k8",    "#8",        /* function key 8 */
  170.     "k9",    "#9",        /* function key 9 */
  171.     NULL
  172. };
  173.  
  174. /*
  175.  * Standout glitch: number of spaces left when entering or leaving
  176.  * standout mode.
  177.  *
  178.  * This abomination is still needed for some terminals (e.g.
  179.  * Televideo).
  180.  */
  181. int        SG;
  182.  
  183. /*
  184.  * Used by scroll region optimisation.
  185.  */
  186. static int    s_top = 0, s_bottom = 0;
  187.  
  188. /*
  189.  * Used for colour-setting optimisation.
  190.  */
  191. #define    NO_COLOUR    -1
  192. static    int        old_colour = NO_COLOUR;
  193.  
  194. /*
  195.  * Flush any pending output, including cursor position.
  196.  */
  197. void
  198. flush_output()
  199. {
  200.     xyupdate();
  201.     oflush();
  202. }
  203.  
  204. /*
  205.  * Put out a "normal" character, updating the cursor position.
  206.  */
  207. void
  208. outchar(c)
  209. register int    c;
  210. {
  211.     xyupdate();
  212.     real_col++;
  213.     virt_col++;
  214.     if (real_col >= CO) {
  215.     if (auto_margins) {
  216.         virt_col = (real_col = 0);
  217.         virt_row = (real_row += 1);
  218.     } else {
  219.         optimise = FALSE;
  220.     }
  221.     }
  222.     moutch(c);
  223. }
  224.  
  225. /*
  226.  * Put out a "normal" string, updating the cursor position.
  227.  */
  228. void
  229. outstr(s)
  230. register char    *s;
  231. {
  232.     xyupdate();
  233.     while (*s != '\0') {
  234.     real_col++;
  235.     virt_col++;
  236.     moutch(*s++);
  237.     }
  238.  
  239.     /*
  240.      * We only worry about whether we have hit the right-hand margin
  241.      * at the end of the string; this is okay so long as we can trust
  242.      * the calling code not to use outstr if the string is going to
  243.      * wrap around.
  244.      */
  245.     if (real_col >= CO) {
  246.     if (auto_margins) {
  247.         virt_col = (real_col %= CO);
  248.         virt_row = (real_row += 1);
  249.     } else {
  250.         optimise = FALSE;
  251.     }
  252.     }
  253. }
  254.  
  255. /*
  256.  * This routine is called by tty_open() if for some reason the terminal
  257.  * is unsuitable.
  258.  */
  259. static void
  260. fail(str)
  261. char    *str;
  262. {
  263.     /*
  264.      * Assume we are in raw mode already, so set back into cooked.
  265.      */
  266.     sys_endv();
  267.  
  268.     (void) fputs(str, stderr);
  269.     putc('\n', stderr);
  270.  
  271.     exit(2);
  272. }
  273.  
  274. /*
  275.  * Look up term entry in termcap database, and set up all the strings.
  276.  */
  277. void
  278. tty_open(prows, pcolumns)
  279. unsigned int    *prows;
  280. unsigned int    *pcolumns;
  281. {
  282.     char    tcbuf[1024];        /* buffer for termcap entry */
  283.     char    *termtype;        /* terminal type */
  284.     static    char strings[512];    /* space for storing strings */
  285.     char    *strp = strings;    /* ptr to space left in strings */
  286.     char    *cp;            /* temp for single char strings */
  287.     int    i;
  288.  
  289.     termtype = getenv("TERM");
  290.     if (termtype == NULL) {
  291.     fail("Can't find your terminal type.");
  292.     }
  293.     switch (tgetent(tcbuf, termtype)) {
  294.     case -1:
  295.     fail("Can't open termcap.");
  296.     /*NOTREACHED*/
  297.     case 0:
  298.     fail("Can't find entry for your terminal in termcap.");
  299.     /*NOTREACHED*/
  300.     }
  301.  
  302.     /*
  303.      * Booleans.
  304.      */
  305.     auto_margins = (bool_t) (tgetflag("am") && !tgetflag("xn"));
  306.     can_backspace = (bool_t) tgetflag("bs");
  307.  
  308.     /*
  309.      * Integers.
  310.      */
  311.  
  312.     /*
  313.      * Screen dimensions. Ask termcap for its values if we haven't
  314.      * already got any.
  315.      */
  316.     if (*pcolumns == 0) {
  317.     int iv;
  318.  
  319.     iv = tgetnum("co");
  320.     if (iv <= 0) {
  321.         fail("`co' entry in termcap is invalid or missing.");
  322.     }
  323.     *pcolumns = CO = (unsigned) iv;
  324.     } else {
  325.     CO = *pcolumns;
  326.     }
  327.     if (*prows == 0) {
  328.     int iv;
  329.  
  330.     iv = tgetnum("li");
  331.     if (iv <= 0) {
  332.         fail("`li' entry in termcap is invalid or missing.");
  333.     }
  334.     *prows = LI = (unsigned) iv;
  335.     } else {
  336.     LI = *prows;
  337.     }
  338.  
  339.     SG = tgetnum("sg");
  340.     if (SG < 0) {
  341.     SG = 0;
  342.     }
  343. #ifdef SG_TEST
  344.     SG++;
  345. #endif
  346.  
  347.     /*
  348.      * Single-char strings - some of these may be strings,
  349.      * but we only want them if they are single characters.
  350.      * This is because the optimisation calculations get
  351.      * extremely complicated if we have to work out the
  352.      * number of characters used to do a cursor move in
  353.      * every possible way; we basically assume that we
  354.      * don't have infinite amounts of time or space.
  355.      */
  356.     cp = tgetstr("pc", &strp);    /* pad character */
  357.     if (cp != NULL)
  358.     PC = *cp;
  359.  
  360.     cp = tgetstr("bc", &strp);    /* backspace char if not ^H */
  361.     if (cp != NULL && cp[1] == '\0')
  362.     BC = *cp;
  363.     else
  364.     BC = '\b';
  365.  
  366.     cp = tgetstr("nd", &strp);    /* non-destructive forward space */
  367.     if (cp != NULL && cp[1] == '\0') {
  368.     ND = *cp;
  369.     can_fwdspace = TRUE;
  370.     }
  371.  
  372. #ifndef    AIX
  373.     /*
  374.      * The termcap emulation (over terminfo) on an RT/PC
  375.      * (the only AIX machine I have experience of) gets
  376.      * the "do" capability wrong; it moves the cursor
  377.      * down a line, but also sends a carriage return.
  378.      * We must therefore avoid use of "do" under AIX.
  379.      */
  380.  
  381.     cp = tgetstr("do", &strp);    /* down a line */
  382.     if (cp != NULL && cp[1] == '\0') {
  383.     DO = *cp;
  384.     can_movedown = TRUE;
  385.     }
  386. #endif
  387.  
  388.     /*
  389.      * Strings.
  390.      */
  391.     KS = tgetstr("ks", &strp);
  392.     KE = tgetstr("ke", &strp);
  393.     VS = tgetstr("vs", &strp);
  394.     VE = tgetstr("ve", &strp);
  395.     TI = tgetstr("ti", &strp);
  396.     TE = tgetstr("te", &strp);
  397.     CE = tgetstr("ce", &strp);
  398.     CL = tgetstr("cl", &strp);
  399.     AL = tgetstr("al", &strp);
  400.     DL = tgetstr("dl", &strp);
  401.     IC = tgetstr("ic", &strp);
  402.     IM = tgetstr("im", &strp);
  403.     EI = tgetstr("ei", &strp);
  404.     CM = tgetstr("cm", &strp);
  405.     HO = tgetstr("ho", &strp);
  406.     CS = tgetstr("cs", &strp);
  407.     sf = tgetstr("sf", &strp);
  408.     sr = tgetstr("sr", &strp);
  409.     SF = tgetstr("SF", &strp);
  410.     SR = tgetstr("SR", &strp);
  411.     SO = tgetstr("so", &strp);
  412.     SE = tgetstr("se", &strp);
  413.     VB = tgetstr("vb", &strp);
  414.  
  415.     /*
  416.      * Find up to 10 colour capabilities.
  417.      */
  418.     for (ncolours = 0; ncolours < 10; ncolours++) {
  419.     char    capname[3];
  420.     char    *cap;
  421.  
  422.     capname[0] = 'c';
  423.     capname[1] = ncolours + '0';    /* assumes ASCII - nasty */
  424.     capname[2] = '\0';
  425.     cap = tgetstr(capname, &strp);
  426.     if (cap == NULL)
  427.         break;
  428.     colours[ncolours] = cap;
  429.     }
  430.  
  431.     if (CM == NULL) {
  432.     fail("Xvi can't work without cursor motion.");
  433.     }
  434.  
  435.     /*
  436.      * This may not be quite right, but it will be close.
  437.      */
  438.     cost_goto = strlen(CM) - 1;
  439.  
  440.     /*
  441.      * Set these variables as appropriate.
  442.      */
  443.     can_del_line = (DL != NULL);
  444.     can_ins_line = (AL != NULL);
  445.     can_inschar = (IC != NULL) || (IM != NULL);
  446.     can_scroll_area = (
  447.     (CS != NULL)
  448.     &&
  449.     (SF != NULL || sf != NULL || DL != NULL || can_movedown)
  450.     &&
  451.     (SR != NULL || sr != NULL || AL != NULL)
  452.     );
  453.  
  454.     /*
  455.      * Enter cursor arrow keys etc into xvi map table.
  456.      */
  457.     for (i = 0; keys[i].key_tcname != NULL; i++) {
  458.     char    *lhs;
  459.  
  460.     lhs = tgetstr(keys[i].key_tcname, &strp);
  461.     if (lhs != NULL) {
  462.         xvi_keymap(lhs, keys[i].key_rhs);
  463.     }
  464.     }
  465. }
  466.  
  467. /*
  468.  * Functions called to perform screen manipulations..
  469.  */
  470. static enum {
  471.     m_SYS = 0,
  472.     m_VI
  473. }    termmode;
  474.  
  475. /*
  476.  * Called by sys_startv(), just after switching to raw/cbreak mode.
  477.  * Assumes tty_open() has been called.
  478.  */
  479. void
  480. tty_startv()
  481. {
  482.     if (termmode == m_SYS) {
  483.     if (TI != NULL)
  484.         tputs(TI, (int) LI, foutch);
  485.     if (VS != NULL)
  486.         tputs(VS, (int) LI, foutch);
  487.     if (KS != NULL)
  488.         tputs(KS, (int) LI, foutch);
  489.     }
  490.     old_colour = NO_COLOUR;
  491.     optimise = FALSE;
  492.     termmode = m_VI;
  493. }
  494.  
  495. /*
  496.  * Called by sys_endv(), just before returning to cooked mode.
  497.  *
  498.  * tty_endv() can be called when we're already in system mode, so we
  499.  * have to check.
  500.  */
  501. void
  502. tty_endv()
  503. {
  504.     if (termmode == m_VI) {
  505.     if (can_scroll_area) {
  506.         set_scroll_region(0, (int) LI - 1);
  507.     }
  508.     if (KE != NULL)
  509.         tputs(KE, (int) LI, foutch);
  510.     if (VE != NULL)
  511.         tputs(VE, (int) LI, foutch);
  512.     if (TE != NULL)
  513.         tputs(TE, (int) LI, foutch);
  514.     termmode = m_SYS;
  515.     }
  516.     oflush();
  517. }
  518.  
  519. /*
  520.  * Erase the entire current line.
  521.  */
  522. void
  523. erase_line()
  524. {
  525.     xyupdate();
  526.     if (CE != NULL)
  527.     tputs(CE, (int) LI, foutch);
  528. }
  529.  
  530. /*
  531.  * Insert one line.
  532.  */
  533. void
  534. insert_line()
  535. {
  536.     xyupdate();
  537.     if (AL != NULL)
  538.     tputs(AL, (int) LI, foutch);
  539. }
  540.  
  541. /*
  542.  * Delete one line.
  543.  */
  544. void
  545. delete_line()
  546. {
  547.     xyupdate();
  548.     if (DL != NULL)
  549.     tputs(DL, (int) LI, foutch);
  550. }
  551.  
  552. /*
  553.  * Erase display (may optionally home cursor).
  554.  */
  555. void
  556. erase_display()
  557. {
  558.     /*
  559.      * Don't know where the cursor goes, so turn optim
  560.      * back off until we can re-sync the position.
  561.      */
  562.     optimise = FALSE;
  563.     old_colour = NO_COLOUR;
  564.     if (CL != NULL)
  565.     tputs(CL, (int) LI, foutch);
  566.     oflush();
  567. }
  568.  
  569. /*
  570.  * Internal routine: used to set the scroll region to an area
  571.  * of the screen. We only change it if necessary.
  572.  *
  573.  * Assumes CS is available, i.e. can_scroll_area is TRUE.
  574.  */
  575. static void
  576. set_scroll_region(top, bottom)
  577. int    top, bottom;
  578. {
  579.     if (top != s_top || bottom != s_bottom) {
  580.     tputs(tgoto(CS, bottom, top), bottom - top, foutch);
  581.     s_top = top;
  582.     s_bottom = bottom;
  583.     /*
  584.      * Some terminals move the cursor when we set scroll region.
  585.      */
  586.     optimise = FALSE;
  587.     }
  588. }
  589.  
  590. /*
  591.  * Scroll up an area of the screen.
  592.  */
  593. void
  594. scroll_up(start_row, end_row, nlines)
  595. int    start_row, end_row, nlines;
  596. {
  597.     if (!can_scroll_area)
  598.     return;
  599.  
  600.     /*
  601.      * Set the scrolling region, if it is different
  602.      * from the one there already. Note that we used
  603.      * to set the scroll region after moving the cursor,
  604.      * because we don't know how the terminal will
  605.      * respond to movements when a scroll region is
  606.      * set; some terminals move relative to the top
  607.      * of the current scroll region. However, some
  608.      * terminals (e.g.vt100) actually move the cursor
  609.      * when the scroll region is set, so you can't win.
  610.      */
  611.     set_scroll_region(start_row, end_row);
  612.  
  613.     /*
  614.      * Make sure we are in the "right" place before calling
  615.      * xyupdate(), or we will get infinite recursion.
  616.      * The "right" place is:
  617.      *    if we have sf or SF:
  618.      *        assume they will work with the cursor placed
  619.      *        anywhere inside the scroll region; so if we
  620.      *        are already there, we don't need to move.
  621.      *        This is a big win for normal editing usage.
  622.      *    if no sf or SF capability:
  623.      *        if we have a dl capability:
  624.      *            the first row of the area
  625.      *            Only move the cursor to that row if it is
  626.      *            not already there; this saves a lot of nasty
  627.      *            "flicker" of the cursor.
  628.      *        else:
  629.      *            we have to use the "do" capability,
  630.      *            on the bottom row of the scroll area.
  631.      *            Assume it is safe to do this, because
  632.      *            optimise will be turned off afterwards,
  633.      *            and the caller save the cursor anyway.
  634.      */
  635.     if (SF != NULL || sf != NULL) {
  636.     if (virt_row < start_row || virt_row > end_row) {
  637.         virt_row = start_row;
  638.         virt_col = 0;
  639.     }
  640.     } else {    /* don't have sf or SF */
  641.     if (DL != NULL) {
  642.         if (virt_row != start_row) {
  643.         virt_row = start_row;
  644.         virt_col = 0;
  645.         }
  646.     } else {    /* no DL; use DO */
  647.         if (virt_row != end_row) {
  648.         virt_row = end_row;
  649.         virt_col = 0;
  650.         }
  651.     }
  652.     }
  653.  
  654.     xyupdate();
  655.  
  656.     /*
  657.      * And scroll the area, either by an explicit sequence
  658.      * or by the appropriate number of line insertions.
  659.      */
  660.     if (SF != NULL && (nlines > 1 || sf == NULL)) {
  661.     static Flexbuf    SFbuf;
  662.  
  663.     flexclear(&SFbuf);
  664.     (void) lformat(&SFbuf, SF, nlines);
  665.     (void) tputs(flexgetstr(&SFbuf), end_row - start_row, foutch);
  666.     } else if (sf != NULL) {
  667.     int    i;
  668.  
  669.     for (i = 0; i < nlines; i++) {
  670.         tputs(sf, end_row - start_row, foutch);
  671.     }
  672.     } else if (DL != NULL) {
  673.     int    i;
  674.  
  675.     for (i = 0; i < nlines; i++) {
  676.         tputs(DL, end_row - start_row, foutch);
  677.     }
  678.     } else {
  679.     int    i;
  680.  
  681.     for (i = 0; i < nlines; i++) {
  682.         moutch(DO);
  683.     }
  684.     }
  685.  
  686.     /*
  687.      * Set the scrolling region back to how it should be.
  688.      */
  689.     set_scroll_region(0, (int) LI - 1);
  690.  
  691.     /*
  692.      * We don't know what this does to the cursor position;
  693.      * so the safest thing to do here is to assume nothing.
  694.      */
  695.     optimise = FALSE;
  696. }
  697.  
  698. /*
  699.  * Scroll down an area of the screen.
  700.  */
  701. void
  702. scroll_down(start_row, end_row, nlines)
  703. int    start_row, end_row, nlines;
  704. {
  705.     if (CS == NULL || (SR == NULL && sr == NULL && AL == NULL))
  706.     return;
  707.  
  708.     /*
  709.      * Set the scrolling region, if it is different
  710.      * from the one there already. Note that we used
  711.      * to set the scroll region after moving the cursor,
  712.      * because we don't know how the terminal will
  713.      * respond to movements when a scroll region is
  714.      * set; some terminals move relative to the top
  715.      * of the current scroll region. However, some
  716.      * terminals (e.g.vt100) actually move the cursor
  717.      * when the scroll region is set, so you can't win.
  718.      */
  719.     set_scroll_region(start_row, end_row);
  720.  
  721.     /*
  722.      * Make sure we are in the "right" place before calling
  723.      * xyupdate(), or we will get infinite recursion.
  724.      * The "right" place is:
  725.      *    if no sr or SR capability:
  726.      *        the first row of the area, so AL will work.
  727.      *        Only move the cursor to that row if it is
  728.      *        not already there; this saves a lot of nasty
  729.      *        "flicker" of the cursor.
  730.      *    if we have sr or SR:
  731.      *        It would be nice to assume that "sr" / "SR"
  732.      *        would work anywhere within the scroll region;
  733.      *        unfortunately, this just isn't true. Sigh.
  734.      *        So we use the first row here too.
  735.      */
  736.     if (virt_row != start_row) {
  737.     virt_row = start_row;
  738.     virt_col = 0;
  739.     }
  740.  
  741.     xyupdate();
  742.  
  743.     /*
  744.      * And scroll the area, either by an explicit sequence
  745.      * or by the appropriate number of line insertions.
  746.      */
  747.     if (SR != NULL && (nlines > 1 || sr == NULL)) {
  748.     static Flexbuf    SRbuf;
  749.  
  750.     flexclear(&SRbuf);
  751.     (void) lformat(&SRbuf, SR, nlines);
  752.     (void) tputs(flexgetstr(&SRbuf), end_row - start_row, foutch);
  753.     } else if (sr != NULL) {
  754.     int    i;
  755.  
  756.     for (i = 0; i < nlines; i++) {
  757.         tputs(sr, end_row - start_row, foutch);
  758.     }
  759.     } else {
  760.     int    i;
  761.  
  762.     for (i = 0; i < nlines; i++) {
  763.         tputs(AL, end_row - start_row, foutch);
  764.     }
  765.     }
  766.  
  767.     /*
  768.      * Set the scrolling region back to how it should be.
  769.      */
  770.     set_scroll_region(0, (int) LI - 1);
  771.  
  772.     /*
  773.      * We don't know what this does to the cursor position;
  774.      * so the safest thing to do here is to assume nothing.
  775.      */
  776.     optimise = FALSE;
  777. }
  778.  
  779. /*
  780.  * Set the specified colour. Just does standout/standend mode for now.
  781.  * Optimisation here to avoid setting standend when we aren't in
  782.  * standout; assumes calling routines are well-behaved (i.e. only do
  783.  * screen movement in P_colour) or some terminals will write garbage
  784.  * all over the screen.
  785.  */
  786. void
  787. set_colour(c)
  788. int    c;
  789. {
  790.     if (c == old_colour)
  791.     return;
  792.  
  793.     xyupdate();
  794.  
  795.     if (c < ncolours) {
  796.     /*
  797.      * Within the range of possible colours.
  798.      */
  799.     tputs(colours[c], 1, foutch);
  800.     } else {
  801.     /*
  802.      * No colour caps, so use standout/standend.
  803.      * Map colour 2..9 => standout, 0 & 1 => normal.
  804.      * This is because the default values are:
  805.      *
  806.      *    systemcolour    0
  807.      *    colour        1
  808.      *    statcolour    2
  809.      *    roscolour    3
  810.      */
  811.  
  812.     if (c == 1)
  813.         c = 0;
  814.  
  815.     if (c == old_colour)
  816.         return;
  817.  
  818.     if (c != 0) {
  819.         if (SO != NULL) {
  820.         tputs(SO, 1, foutch);
  821. #ifdef SG_TEST
  822.         outchar('+');
  823. #endif
  824.         }
  825.     } else {
  826.         if (SE != NULL) {
  827. #ifdef SG_TEST
  828.         outchar('-');
  829. #endif
  830.         tputs(SE, 1, foutch);
  831.         }
  832.     }
  833.     }
  834.  
  835.     old_colour = c;
  836. }
  837.  
  838. /*
  839.  * Insert the given character at the cursor position.
  840.  */
  841. void
  842. inschar(c)
  843. char    c;
  844. {
  845.     xyupdate();
  846.     if (IM != NULL)
  847.     tputs(IM, (int) LI, foutch);
  848.     if (IC != NULL)
  849.     tputs(IC, (int) LI, foutch);
  850.     outchar(c);
  851.     if (EI != NULL)
  852.     tputs(EI, (int) LI, foutch);
  853. }
  854.  
  855. /*
  856.  * Goto the specified location.
  857.  */
  858. void
  859. tty_goto(row, col)
  860. int    row, col;
  861. {
  862.     virt_row = row;
  863.     virt_col = col;
  864. }
  865.  
  866. /*
  867.  * Output a linefeed.
  868.  *
  869.  * This can only be called safely when the virtual cursor is at the
  870.  * bottom left corner of the display area.
  871.  */
  872. void
  873. tty_linefeed()
  874. {
  875.     xyupdate();
  876.     moutch('\n');
  877. }
  878.  
  879. /*
  880.  * Beep at the user.
  881.  *
  882.  * Use visual bell if it's there and the vbell parameter says to use it.
  883.  */
  884. void
  885. alert()
  886. {
  887.     if (Pb(P_vbell) && VB != NULL) {
  888.     xyupdate();
  889.     tputs(VB, (int) LI, foutch);
  890.     optimise = FALSE;
  891.     } else {
  892.     moutch('\007');
  893.     }
  894.     oflush();
  895. }
  896.  
  897. /*
  898.  * This is an internal routine which is called whenever we want
  899.  * to be sure that the cursor position is correct; it looks at
  900.  * the cached values of the desired row and column, compares them
  901.  * with the recorded "real" row and column, and outputs whatever
  902.  * escape codes are necessary to put the cursor in the right place.
  903.  *
  904.  * Some optimisation is done here, because quite often this can be
  905.  * a big win; many cursor movements asked for by the editor are
  906.  * quite simple to do, only costing a few output bytes and little
  907.  * work.  If "optimise" is FALSE, this optimisation is not done.
  908.  * Other routines in this file can use this to turn optimisation
  909.  * off temporarily if they "lose" the cursor.
  910.  */
  911. static void
  912. xyupdate()
  913. {
  914.     register int    hdisp, vdisp;
  915.     register int    totaldisp;
  916.  
  917.     /*
  918.      * Horizontal and vertical displacements needed
  919.      * to get the cursor to the right position.
  920.      * These are positive for downward and rightward movements.
  921.      *
  922.      * Totaldisp is the total absolute displacement.
  923.      */
  924.     hdisp = virt_col - real_col;
  925.     vdisp = virt_row - real_row;
  926.  
  927.     totaldisp = ((vdisp < 0) ? -vdisp : vdisp) +
  928.         ((hdisp < 0) ? -hdisp : hdisp);
  929.  
  930.     /*
  931.      * First, ensure that the current scroll region
  932.      * contains the intended cursor position.
  933.      */
  934.     if (virt_row < s_top || virt_row > s_bottom) {
  935.     if (can_scroll_area)
  936.         set_scroll_region(0, (int) LI - 1);
  937.     }
  938.  
  939.     /*
  940.      * If we want to go near the top of the screen, it may be
  941.      * worth using HO.  We musn't do this if we would thereby
  942.      * step outside the current scroll region.  Also, we can
  943.      * only move to a position near home if we can use "down"
  944.      * and "right" movements after having gone to "home".
  945.      */
  946.     if (
  947.     (
  948.         HO != NULL        /* "home" capability exists */
  949.     )
  950.     &&                /* AND */
  951.     (
  952.         !can_scroll_area    /* no scroll regions */
  953.         ||
  954.         s_top == 0        /* or doesn't affect us */
  955.     )
  956.     &&                /* AND */
  957.     (
  958.         virt_col == 0        /* we're not moving right */
  959.         ||
  960.         can_fwdspace        /* or we can if we want */
  961.     )
  962.     &&                /* AND */
  963.     (
  964.         virt_row == 0        /* we're not moving down */
  965.         ||
  966.         can_movedown        /* or we can if we want */
  967.     )
  968.     ) {
  969.     /*
  970.      * Cost of using the "ho" capability.
  971.      */
  972.     static unsigned        cost_home;
  973.  
  974.     /*
  975.      * Possible total cost of getting to the desired
  976.      * position if we use "ho".
  977.      */
  978.     register unsigned    netcost;
  979.  
  980.     if (cost_home == 0)
  981.         cost_home = strlen(HO);
  982.     netcost = cost_home + virt_row + virt_col;
  983.  
  984.     /*
  985.      * Only use home if it is worth it, and if
  986.      * either we are already below where we want
  987.      * to be on the screen, or optimise is off
  988.      * (and hence relative movements inappropriate).
  989.      */
  990.     if (netcost < cost_goto
  991.         &&
  992.         (!optimise || real_row > virt_row)) {
  993.         tputs(HO, (int) LI, foutch);
  994.         real_row = real_col = 0;
  995.         totaldisp = (hdisp = virt_col) + (vdisp = virt_row);
  996.         optimise = TRUE;
  997.     }
  998.     }
  999.  
  1000.     if (!optimise) {
  1001.     /*
  1002.      * If optim is off, we should just go to the
  1003.      * specified place; we can then turn it on,
  1004.      * because we know where the cursor is.
  1005.      */
  1006.     tputs(tgoto(CM, virt_col, virt_row), (int) LI, foutch);
  1007.     optimise = TRUE;
  1008.     } else {
  1009.     if (vdisp != 0 || hdisp != 0) {
  1010.         /*
  1011.          * Update the cursor position in the best way.
  1012.          */
  1013.  
  1014.         if (
  1015.         (totaldisp < cost_goto)
  1016.         &&
  1017.         (
  1018.             hdisp == 0
  1019.             ||
  1020.             (hdisp > 0 && can_fwdspace)
  1021.             ||
  1022.             (hdisp < 0 && can_backspace)
  1023.         )
  1024.         &&
  1025.         (
  1026.             vdisp >= 0
  1027.             &&
  1028.             can_movedown
  1029.         )
  1030.         ) {
  1031.         /*
  1032.          * A small motion; worth looking at
  1033.          * doing it with BS, ND and DO.
  1034.          * No UP handling yet, and we don't
  1035.          * really care about whether ND is
  1036.          * more than one char - this can make
  1037.          * the output really inefficient.
  1038.          */
  1039.  
  1040.         int    n;
  1041.  
  1042.         /*
  1043.          * Move down to the right line.
  1044.          */
  1045.         for (n = vdisp; n > 0; n--) {
  1046.             moutch(DO);
  1047.         }
  1048.  
  1049.         if (hdisp < 0) {
  1050.             if (virt_col == 0) {
  1051.             moutch('\r');
  1052.             } else {
  1053.             for (n = hdisp; n < 0; n++) {
  1054.                 moutch(BC);
  1055.             }
  1056.             }
  1057.         } else if (hdisp > 0) {
  1058.             for (n = hdisp; n > 0; n--) {
  1059.             moutch(ND);
  1060.             }
  1061.         }
  1062.  
  1063.         } else if (vdisp == 0) {
  1064.  
  1065.         /*
  1066.          * Same row.
  1067.          */
  1068.         if (virt_col == 0) {
  1069.             /*
  1070.              * Start of line - easy.
  1071.              */
  1072.             moutch('\r');
  1073.  
  1074.         } else if (can_fwdspace && hdisp > 0 &&
  1075.                 hdisp < cost_goto) {
  1076.             int    n;
  1077.  
  1078.             /*
  1079.              * Forward a bit.
  1080.              */
  1081.             for (n = hdisp; n > 0; n--) {
  1082.             moutch(ND);
  1083.             }
  1084.  
  1085.         } else if (can_backspace && hdisp < 0 &&
  1086.                 (-hdisp) < cost_goto) {
  1087.             int    n;
  1088.  
  1089.             /*
  1090.              * Back a bit.
  1091.              */
  1092.             for (n = hdisp; n < 0; n++) {
  1093.             moutch(BC);
  1094.             }
  1095.  
  1096.         } else {
  1097.             /*
  1098.              * Move a long way.
  1099.              */
  1100.             tputs(tgoto(CM, virt_col, virt_row),
  1101.                 1, foutch);
  1102.         }
  1103.         } else if (virt_col == 0) {
  1104.  
  1105.         /*
  1106.          * Different row, column 0.
  1107.          */
  1108.         if (vdisp > 0 && vdisp + 1 < cost_goto) {
  1109.             /*
  1110.              * Want to move downwards.
  1111.              * This happens a lot.
  1112.              */
  1113.             int    n;
  1114.  
  1115.             if (real_col != 0)
  1116.             moutch('\r');
  1117.             for (n = vdisp; n > 0; n--) {
  1118.             moutch('\n');
  1119.             }
  1120.         } else {
  1121.             /*
  1122.              * Want to move upwards.
  1123.              */
  1124.             tputs(tgoto(CM, virt_col, virt_row),
  1125.                 (int) LI, foutch);
  1126.         }
  1127.         } else {
  1128.         /*
  1129.          * Give up - do a goto.
  1130.          */
  1131.         tputs(tgoto(CM, virt_col, virt_row),
  1132.             (int) LI, foutch);
  1133.         }
  1134.     }
  1135.     }
  1136.     real_row = virt_row;
  1137.     real_col = virt_col;
  1138. }
  1139.